﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Threading;
using DiscoveryLogic.Analytics.DataObjects;
using DiscoveryLogic.Analytics.DataObjects.Visuals;

namespace DiscoveryLogic.Analytics.Modules.Diagrams.GravityDiagrams
{
    public class ParticleEventArgs : RoutedEventArgs
    {
        private Particle _Particle;
        /// <summary></summary>
        public Particle Particle
        {
            get
            {
                return this._Particle;
            }
            set
            {
                this._Particle = value;
            }
        }

        public ParticleEventArgs(RoutedEvent routedEvent, Particle particle)
            : base(routedEvent)
        {
            this._Particle = particle;
        }
    }

    public class GravityParticleCanvas : Canvas
    {
        #region events
        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public delegate void ParticleReleasedEventHandler(object sender, ParticleEventArgs e);

        public static readonly RoutedEvent ParticleReleasedEvent =
            EventManager.RegisterRoutedEvent("ParticleReleased",
                                             RoutingStrategy.Bubble,
                                             typeof(ParticleReleasedEventHandler),
                                             typeof(GravityParticleCanvas));

        public event ParticleReleasedEventHandler ParticleReleased
        {
            add { AddHandler(ParticleReleasedEvent, value); }
            remove { RemoveHandler(ParticleReleasedEvent, value); }
        }

        private void FireParticleReleasedEvent(Particle particle)
        {
            ParticleEventArgs args = new ParticleEventArgs(ParticleReleasedEvent, particle);
            RaiseEvent(args);
        }

        public delegate void OnParticleSelected(Particle particle);

        public event OnParticleSelected ParticleSelected;
        #endregion

        #region fields
        private readonly DispatcherTimer _WorldTimer = new DispatcherTimer();
        private Point _OwnerWindowPosition;
        private Point _PrevAbsoluteMousePosition;
        private Particle _SelectedParticle = null;
        private double _SelectedParticleMass = 0.0f;
        private int _ZIndex = 0;
        private int _Iteration = 0;
        #endregion

        #region props
        private string _NetworkName;
        /// <summary></summary>
        public string NetworkName
        {
            get
            {
                return this._NetworkName;
            }
            set
            {
                this._NetworkName = value;
            }
        }

        private NetworkVisual _NetVisual = NetworkVisual.Default;
        /// <summary></summary>
        public NetworkVisual NetVisual
        {
            get
            {
                return this._NetVisual;
            }
            set
            {
                this._NetVisual = value;
            }
        }

        private bool _IsStopped;
        /// <summary></summary>
        public bool IsStopped
        {
            get
            {
                return this._IsStopped;
            }
            set
            {
                if (this._IsStopped != value)
                {
                    this._IsStopped = value;
                }
                if (this._WorldTimer.IsEnabled && this._IsStopped)
                {
                    this._WorldTimer.IsEnabled = false;
                }
            }
        }

        private ParticleSystem _ParticleSystem;
        /// <summary></summary>
        public ParticleSystem ParticleSystem
        {
            get
            {
                return this._ParticleSystem;
            }
            set
            {
                this._ParticleSystem = value;
            }
        }

        private bool _RenderParticleSystem;
        /// <summary></summary>
        public bool RenderParticleSystem
        {
            get
            {
                return this._RenderParticleSystem;
            }
            set
            {
                this._RenderParticleSystem = value;
            }
        }

        private Window _OwnerWindow;
        /// <summary></summary>
        public Window OwnerWindow
        {
            get
            {
                return this._OwnerWindow;
            }
            set
            {
                if (value != null)
                {
                    this._OwnerWindow = value;
                    this._OwnerWindowPosition = new Point(0, 0);
                    this._OwnerWindow.LocationChanged += new EventHandler(_OwnerWindow_LocationChanged);
                }
            }
        }

        void _OwnerWindow_LocationChanged(object sender, EventArgs e)
        {
            Vector deltaMovement = new Vector(this._OwnerWindowPosition.X - this._OwnerWindow.Left,
                this._OwnerWindowPosition.Y - this._OwnerWindow.Top);
            this._OwnerWindowPosition = new Point(this._OwnerWindow.Left, this._OwnerWindow.Top);
            foreach (Particle particle in this.ParticleSystem.Particles)
            {
                particle.MoveToPosition(deltaMovement);
            }
        }
        #endregion

        #region ctor
        public GravityParticleCanvas()
        {
            this._WorldTimer.Interval = new TimeSpan(50);
            this._WorldTimer.Tick += new EventHandler(_WorldTimer_Tick);
            this._ParticleSystem = new ParticleSystem();
        }
        #endregion

        #region layout
        protected override Size ArrangeOverride(Size arrangeSize)
        {
            foreach (UIElement element in this.InternalChildren)
            {
                double left = Canvas.GetLeft(element);
                double top = Canvas.GetTop(element);
                double x = double.IsNaN(left) ? 0 : left;
                double y = double.IsNaN(top) ? 0 : top;
                element.Arrange(new Rect(new Point(x, y), element.DesiredSize));
            }
            return arrangeSize;
        }

        protected override Size MeasureOverride(Size constraint)
        {
            Size size = new Size(double.PositiveInfinity, double.PositiveInfinity);
            foreach (UIElement element in this.InternalChildren)
            {
                element.Measure(size);
            }
            return new Size();
        }

        protected override void OnRender(System.Windows.Media.DrawingContext dc)
        {
            base.OnRender(dc);
            this.ParticleSystem.Render(dc);
        }
        #endregion

        #region drag-drop
        public void Canvas_MouseUp(object sender, MouseButtonEventArgs e)
        {
            if (e.ButtonState == MouseButtonState.Released)
            {
                if (this._SelectedParticle != null)
                {
                    this._SelectedParticle.Mass = this._SelectedParticleMass;
                    this.FireParticleReleasedEvent(this._SelectedParticle);
                    if (this._IsStopped)
                    {
                        this._SelectedParticle.SnapControl();
                        this.InvalidateVisual();
                    }
                }
            }
        }

        public void Canvas_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.LeftButton == MouseButtonState.Pressed)
            {
                if (this._SelectedParticle != null)
                {
                    Point absolutePosition = Mouse.GetPosition(this);
                    Rect constraintRect = new Rect(0, 0, this.ActualWidth, this.ActualHeight);
                    this._SelectedParticle.SetPosition(
                        new Vector(
                            this._SelectedParticle.Position.X + (absolutePosition.X - this._PrevAbsoluteMousePosition.X),
                            this._SelectedParticle.Position.Y + (absolutePosition.Y - this._PrevAbsoluteMousePosition.Y)),
                        constraintRect);
                    if (this._IsStopped)
                    {
                        this._SelectedParticle.SnapControl();
                        this.InvalidateVisual();
                    }
                    this._PrevAbsoluteMousePosition = absolutePosition;
                }
            }
        }

        public void Canvas_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.LeftButton == MouseButtonState.Pressed)
            {
                this._PrevAbsoluteMousePosition = Mouse.GetPosition(this);
                var particleWhere = from particle in this.ParticleSystem.Particles
                                    where particle.Control == sender
                                    select particle;
                if (particleWhere.Count() > 0)
                {
                    Particle particle = particleWhere.First();
                    if (this._SelectedParticle != null)
                        this._SelectedParticle.Mass = this._SelectedParticleMass;
                    this._SelectedParticleMass = particle.Mass;
                    this._SelectedParticle = particle;
                    this._SelectedParticle.Mass = Single.PositiveInfinity;
                    this._SelectedParticle.Velocity = new Vector();
                    this._SelectedParticle.Control.SetValue(Canvas.ZIndexProperty, this._ZIndex++);

                    if (this.ParticleSelected != null)
                        this.ParticleSelected(this._SelectedParticle);

                    return;
                }
            }
        }

        public void Canvas_MouseEnter(object sender, MouseEventArgs e)
        {
            (sender as Control).SetValue(Canvas.ZIndexProperty, this._ZIndex++);
        }
        #endregion

        #region simulation
        public void StartSimulation()
        {
            if (!this._WorldTimer.IsEnabled)
            {
                this._IsStopped = false;
                this._Iteration = 0;
                this._WorldTimer.IsEnabled = true;
            }
        }

        public void StopSimulation()
        {
            this._WorldTimer.IsEnabled = false;
        }

        void _WorldTimer_Tick(object sender, EventArgs e)
        {
            this._Iteration++;

            Rect constraintRect = new Rect(0, 0, this.ActualWidth, this.ActualHeight);
            lock (this._ParticleSystem.Particles)
            {
                int particleMoved = this.ParticleSystem.DoEulerStep(0.005f, constraintRect);
                if (particleMoved == 0 && this._Iteration > 200)
                {
                    this.IsStopped = true;
                }
                foreach (Particle particle in this.ParticleSystem.Particles)
                {
                    particle.SnapControl();
                }
            }
            this.InvalidateVisual();
        }
        #endregion

        #region select
        public void SelectEntity(EntityBase entity)
        {

        }
        #endregion
    }
}
